﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="InstallController.cs" company="ecblog.codeplex.com">
//   emrecoskun.net - ibrahimatay.com
// </copyright>
// <summary>
//   Install controller
// </summary>
// --------------------------------------------------------------------------------------------------------------------
 
namespace EcBlog.Controllers
{
    using System;
    using System.Configuration;
    using System.Data.Common;
    using System.Data.SqlClient;
    using System.Data.SqlServerCe;
    using System.Linq;
    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Web.Script.Serialization;

    using EcBlog.Data.Sql;
    using EcBlog.Data.Sql.Base;
    using EcBlog.Models;
    using EcBlog.Properties;

    using Elmah;

    using WebMatrix.WebData;

    /// <summary>
    /// Install controller
    /// </summary>
    public class InstallController : Controller
    {
        #region Core Actions

        /// <summary>
        /// Indexes this instance.
        /// </summary>
        /// <returns>The installation view.</returns>
        public ActionResult Index()
        {
            if (Migrations.Configuration.HasSetupCompleted())
            {
                ReinstallRouteTable();
                return this.RedirectToRoute("Default");
            }
            
            Migrations.Configuration.Preinstall(new EcUnitOfWork(new EcModelContext()));
            this.ViewData["DataSourceList"] = UnitOfWork.GetEnumKeyValueList<DataSource>();
            var model = GenerateInstallViewModel();
            return this.View(@"~/Views/Install/Index.cshtml", @"~/Areas/Admin/Views/Shared/_InstallLayout.cshtml", model);
        }

        /// <summary>
        /// Confirms the specified view model.
        /// </summary>
        /// <param name="viewModel">The view model.</param>
        /// <returns>The confirm view.</returns>
        public ActionResult Confirm(InstallViewModel viewModel)
        {
            return this.PartialView(viewModel);
        }

        /// <summary>
        /// Completes the specified view model.
        /// </summary>
        /// <param name="viewModel">The view model.</param>
        /// <returns>The instalation result message.</returns>
        [HttpPost]
        public ActionResult Complete(InstallViewModel viewModel)
        {
            if (this.ModelState.IsValid)
            {
                try
                {
                    var path = System.IO.Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), "install.txt");
                    if (System.IO.File.Exists(path))
                    {
                        var serializer = new JavaScriptSerializer();
                        var content = System.IO.File.ReadAllText(path);
                        var savedViewModel = serializer.Deserialize<InstallViewModel>(content);
                        if (savedViewModel.Equals(viewModel))
                        {
                            ComplateInstallation(viewModel);
                            ReinstallRouteTable();
                            System.IO.File.Delete(path);
                            return this.Json(ModelLocalization.OK, JsonRequestBehavior.AllowGet);
                        }
                    } 
                }
                catch (Exception e)
                {
                    return this.Json(ModelLocalization.Fail + ":" + e.Message, JsonRequestBehavior.AllowGet);
                }
            }

            return this.Json(ModelLocalization.Fail, JsonRequestBehavior.AllowGet);
        }
        #endregion

        #region AJAX Support Methods 
        
        /// <summary>
        /// Saves the config and recyle application.
        /// </summary>
        /// <param name="viewModel">The view model.</param>
        /// <returns>The recycle application result message.</returns>
        public ActionResult SaveConfigAndRecyleApplication(InstallViewModel viewModel)
        {
            try
            {
                var webConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
                var connectionStringConfig = CreateConnectionStringSetting(
                    viewModel.DataSource, viewModel.ConnectionString);
                webConfig.ConnectionStrings.ConnectionStrings.Remove("DefaultConnection");
                webConfig.ConnectionStrings.ConnectionStrings.Add(connectionStringConfig);

                webConfig.Save(ConfigurationSaveMode.Modified); // Note the application will recyled

                var serializer = new JavaScriptSerializer();
                var path = System.IO.Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), "install.txt");
                var content = serializer.Serialize(viewModel);
                System.IO.File.WriteAllText(path, content);
                return this.Json(ModelLocalization.InstalationFirstStepOk, JsonRequestBehavior.AllowGet);
            }
            catch (Exception e)
            {
                ErrorSignal.FromCurrentContext().Raise(e);
                this.Response.StatusCode = 500;
                this.Response.AppendHeader("message",  e.Message);
                return this.Json(ModelLocalization.Fail, JsonRequestBehavior.AllowGet);
            }
        }

        /// <summary>
        /// Checks the connection.
        /// </summary>
        /// <param name="dataSource">The data source.</param>
        /// <param name="connectionString">The connection string.</param>
        /// <returns>The database connection result.</returns>
        public ActionResult CheckConnection(string dataSource, string connectionString)
        {
            try
            {
                var needRemove = false;
                if (dataSource == "SqlCe")
                {
                    var path = GetSqlCeFilePath(connectionString);
                    if (!System.IO.File.Exists(path))
                    {
                        var engine = new SqlCeEngine(connectionString);
                        engine.CreateDatabase();
                        needRemove = true;
                    }
                }

                DbConnection connection = CreateConnection(dataSource, connectionString);
                connection.Open();
                connection.Close();

                if (dataSource == "SqlCe" && needRemove)
                {
                    var path = GetSqlCeFilePath(connectionString);
                    System.IO.File.Delete(path);
                }

                return this.Json(ModelLocalization.OK, JsonRequestBehavior.AllowGet);
            }
            catch (Exception)
            {
                return this.Json(ModelLocalization.Fail, JsonRequestBehavior.AllowGet);
            }
        } 

        #endregion

        #region Support Methods
        private static void ComplateInstallation(InstallViewModel viewModel)
        {
            var context = new EcModelContext();
            var unitOfWork = new EcUnitOfWork(context);
            Migrations.Configuration.ComplateSetup(unitOfWork);

            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "SiteUrl").SettingValue =
                viewModel.SiteUrl;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "SiteTitle").SettingValue =
                viewModel.SiteTitle;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "SiteSlogan").SettingValue =
                viewModel.SiteSlogan;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "SiteDescription").SettingValue =
                viewModel.SiteDescription;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "Copyright").SettingValue =
                viewModel.Copyright;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "Author").SettingValue =
                viewModel.Author;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "AdminAuthenticationId").SettingValue =
                viewModel.AdminUserName;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "Email").SettingValue =
                viewModel.AdminEmail;
            unitOfWork.SiteSettingRepository.Get().First(c => c.SettingKey == "SetupCompleted").SettingValue =
                bool.TrueString;
            unitOfWork.Save();
            
            try
            {
                WebSecurity.InitializeDatabaseConnection("DefaultConnection", "Users", "UserId", "UserName", autoCreateTables: true);
            }catch(Exception){}
            WebSecurity.CreateUserAndAccount(viewModel.AdminUserName, viewModel.AdminPassword);
            var roleProvider = new SimpleRoleProvider();
            roleProvider.AddUsersToRoles(new[] { viewModel.AdminUserName }, new[] { PortalRole.Admin.ToString() });
            WebSecurity.Login(viewModel.AdminUserName, viewModel.AdminPassword); 
            
            ReinstallRouteTable();
        }

        private static InstallViewModel GenerateInstallViewModel()
        {
            var result = new InstallViewModel();
            if (IsExistsConnectionString())
            {
                result.DataSource = GetDataSource(System.Web.Configuration.WebConfigurationManager.ConnectionStrings["DefaultConnection"].ProviderName);
                result.ConnectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
            }
            else
            {
                result.DataSource = "SqlCe";
                result.ConnectionString = @"Data Source = |DataDirectory|\EC.sdf";
            }

            if (IsExistsDatabase())
            {
                var unitOfWork = new EcUnitOfWork(new EcModelContext());
                result.SiteUrl = unitOfWork.SiteSettingRepository[SiteSettingKey.SiteUrl];
                result.SiteDescription = unitOfWork.SiteSettingRepository[SiteSettingKey.SiteDescription];
                result.SiteTitle = unitOfWork.SiteSettingRepository[SiteSettingKey.SiteTitle];
                result.SiteSlogan = unitOfWork.SiteSettingRepository[SiteSettingKey.SiteSlogan];
                result.Author = unitOfWork.SiteSettingRepository[SiteSettingKey.Author];
                result.Copyright = unitOfWork.SiteSettingRepository[SiteSettingKey.Copyright]; 
                result.AdminUserName = "admin"; 
            }
            else
            {
                result.SiteUrl = Migrations.Configuration.GetSiteDefault(SiteSettingKey.SiteUrl);
                result.SiteDescription = Migrations.Configuration.GetSiteDefault(SiteSettingKey.SiteDescription);
                result.SiteTitle = Migrations.Configuration.GetSiteDefault(SiteSettingKey.SiteTitle);
                result.SiteSlogan = Migrations.Configuration.GetSiteDefault(SiteSettingKey.SiteSlogan);
                result.Author = Migrations.Configuration.GetSiteDefault(SiteSettingKey.Author);
                result.Copyright = Migrations.Configuration.GetSiteDefault(SiteSettingKey.Copyright);
                result.AdminUserName = "admin";
            }

            return result;
        }

        private static void ReinstallRouteTable()
        {
            RouteTable.Routes.Clear();
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

        private static string GetDataSource(string providerName)
        {
            return providerName == "System.Data.SqlServerCe.4.0" ? "SqlCe" : "SqlServer";
        }

        private static string GetProviderName(string dataSource)
        {
            return dataSource == "SqlCe" ? "System.Data.SqlServerCe.4.0" : "System.Data.SqlClient";
        }

        private static bool IsExistsDatabase()
        {
            try
            {
                var context = new EcModelContext();
                return context.Database.Exists();
            }
            catch (Exception)
            {
                return false;
            }
        }

        private static bool IsExistsConnectionString()
        {
            try
            {
                return
                    !string.IsNullOrEmpty(
                        System.Web.Configuration.WebConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
            }
            catch (Exception)
            {
                return false;
            }
        }

        private static DbConnection CreateConnection(string dataSource, string connectionString, bool forceCreate = false)
        {
            switch (dataSource)
            {
                case "SqlCe":
                    {
                        if (forceCreate)
                        {
                            var path = GetSqlCeFilePath(connectionString);
                            if (!System.IO.File.Exists(path))
                            {
                                var engine = new SqlCeEngine(connectionString);
                                engine.CreateDatabase();
                            }
                        }

                        return new SqlCeConnection(connectionString);
                    }

                case "SqlServer":
                    return new SqlConnection(connectionString);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private static ConnectionStringSettings CreateConnectionStringSetting(string dataSource, string connectionString)
        {
            var connectionStringConfig = new ConnectionStringSettings(
                "DefaultConnection", connectionString, GetProviderName(dataSource));
            return connectionStringConfig;
        }

        private static string GetSqlCeFilePath(string connectionString)
        {
            var path = connectionString.Replace("|DataDirectory|", AppDomain.CurrentDomain.GetData("DataDirectory").ToString());
            var s = path.IndexOf(@":\", StringComparison.Ordinal) - 1;
            var e = path.ToLower().IndexOf(".sdf", StringComparison.Ordinal) - s + 4;
            path = path.Substring(s, e);
            return path;
        }
        #endregion
    }
}
